package com.clear.subject.domain.service.impl;

import com.alibaba.fastjson.JSON;
import com.clear.subject.common.enums.IsDeletedFlagEnum;
import com.clear.subject.domain.convert.SubjectCategoryConverter;
import com.clear.subject.domain.convert.SubjectLabelConverter;
import com.clear.subject.domain.entity.SubjectCategoryBO;
import com.clear.subject.domain.entity.SubjectLabelBO;
import com.clear.subject.domain.service.SubjectCategoryDomainService;
import com.clear.subject.domain.util.CacheUtil;
import com.clear.subject.infra.basic.entity.SubjectCategory;
import com.clear.subject.infra.basic.entity.SubjectLabel;
import com.clear.subject.infra.basic.entity.SubjectMapping;
import com.clear.subject.infra.basic.service.SubjectCategoryService;
import com.clear.subject.infra.basic.service.SubjectLabelService;
import com.clear.subject.infra.basic.service.SubjectMappingService;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.collections4.MapUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SubjectCategoryDomainServiceImpl implements SubjectCategoryDomainService {

    @Resource
    public SubjectCategoryService subjectCategoryService;

    @Resource
    private SubjectMappingService subjectMappingService;

    @Resource
    private SubjectLabelService subjectLabelService;

    @Resource
    private ThreadPoolExecutor labelThreadPool;

    @Resource
    private CacheUtil cacheUtil;

    /**
     * 新增分类
     *
     * @param subjectCategoryBO
     */
    public void add(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryController.add.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        // 新增的分类is_delete字段设置为0
        //  两种方法：一：手动setter
        //          二：在数据库表中为is_delete字段设置默认值
        subjectCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETE.getCode());

        subjectCategoryService.insert(subjectCategory);
    }


    /**
     * 查询分类
     *
     * @param subjectCategoryBO
     * @return
     */
    public List<SubjectCategoryBO> queryCategory(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainServiceImpl.queryCategory.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }
        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        // 只查询未删除
        subjectCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETE.getCode());

        List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);

        List<SubjectCategoryBO> boList = SubjectCategoryConverter.INSTANCE.convertCategoryListToBOList(subjectCategoryList);
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainServiceImpl.queryCategory.boList:{}", JSON.toJSONString(boList));
        }
        // 为每个分类设置题目数量
        boList.forEach(bo -> {
            Integer subjectCount = subjectCategoryService.querySubjectCount(bo.getId());
            bo.setCount(subjectCount);
        });
        return boList;
    }

    /**
     * 更新分类
     *
     * @param subjectCategoryBO
     * @return
     */
    @Override
    public Boolean update(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainServiceImpl.update.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }

        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        int count = subjectCategoryService.update(subjectCategory);

        return count > 0;
    }

    /**
     * 删除分类
     *
     * @param subjectCategoryBO
     * @return
     */
    @Override
    public Boolean delete(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainServiceImpl.delete.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }

        SubjectCategory subjectCategory = SubjectCategoryConverter.INSTANCE.convertBoToCategory(subjectCategoryBO);
        subjectCategory.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());
        // todo 逻辑删除：调用update，而不是deleteById
        int count = subjectCategoryService.update(subjectCategory);

        return count > 0;
    }

    /**
     * 根据大类id 查询二级分类及标签
     *
     * @param subjectCategoryBO
     * @return
     */
    @Override
    @SneakyThrows
    public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBO) {
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainServiceImpl.queryCategoryAndLabel.bo:{}", JSON.toJSONString(subjectCategoryBO));
        }
        Long id = subjectCategoryBO.getId();    // 大类id
        // 查缓存
        String cacheKey = "categoryAndLabel." + subjectCategoryBO.getId();
        List<SubjectCategoryBO> subjectCategoryBOS = cacheUtil.getResult(cacheKey, SubjectCategoryBO.class,
                (key) -> getSubjectCategoryBOS(id)  // 缓存查不到，才执行function
        );

        return subjectCategoryBOS;
    }

    /**
     * <p>
     * 根据大类id 查询二级分类及其标签
     * </p>
     *
     * @param categoryId 父类分类id 大类id
     * @return
     */
    private List<SubjectCategoryBO> getSubjectCategoryBOS(Long categoryId) {
        SubjectCategory subjectCategory = new SubjectCategory();
        subjectCategory.setParentId(categoryId);
        subjectCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETE.getCode());    // 只查询未删除
        // 1.查询当前大类下所有二级分类
        List<SubjectCategory> subjectCategoryList = subjectCategoryService.queryCategory(subjectCategory);
        if (log.isInfoEnabled()) {
            log.info("SubjectCategoryDomainServiceImpl.queryCategoryAndLabel.subjectCategoryList:{}", JSON.toJSONString(subjectCategoryList));
        }
        List<SubjectCategoryBO> subjectCategoryBOList = SubjectCategoryConverter.INSTANCE.convertCategoryListToBOList(subjectCategoryList);

        // 2 依次获取标签信息
        // ==================================================================================================
        //      我们想把下面这段foreach中的同步操作变为并发（异步）操作
        //      即每个分类去查标签，改写为多个分类同时去查标签
        // ==================================================================================================
        Map<Long, List<SubjectLabelBO>> map = new HashMap<>();
        /**
         * CompletableFuture.supplyAsync 异步执行任务
         * 参数一：supplier供应函数
         * 参数二（可选）：线程池，如果不给则使用默认的线程池，建议给出，达到线程池隔离
         */
        List<CompletableFuture<Map<Long, List<SubjectLabelBO>>>> completableFutureList = subjectCategoryBOList.stream().map(categoryBO ->
                CompletableFuture.supplyAsync(() -> getLabelBOList(categoryBO), labelThreadPool)    // 使用labelThreadPool线程池执行任务（并发的去查询各个二级分类下的标签）
        ).collect(Collectors.toList());
        // 3.获取异步执行结果，存放在map中
        completableFutureList.forEach(future -> {
            try {
                /**
                 * CompletableFuture.get 阻塞等待结果
                 */
                Map<Long, List<SubjectLabelBO>> resultMap = future.get();   // 获取线程执行结果
                if (!MapUtils.isEmpty(resultMap)) {
                    map.putAll(resultMap);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        });
        // 4.组装数据，遍历subjectCategoryBOList，将标签label组合进去
        subjectCategoryBOList.forEach(categoryBO -> {
            // 组装：将label列表放入到分类
            if (!CollectionUtils.isEmpty(map.get(categoryBO.getId()))) {
                categoryBO.setSubjectLabelBOList(map.get(categoryBO.getId()));
            }
        });

        return subjectCategoryBOList;
    }


    private Map<Long, List<SubjectLabelBO>> getLabelBOList(SubjectCategoryBO categoryBO) {
        if (log.isInfoEnabled()) {
            log.info("getLabelBOList.categoryBO:{}", JSON.toJSONString(categoryBO));
        }
        Map<Long, List<SubjectLabelBO>> labelMap = new HashMap<>();

//        // 封装subjectMapping
//        SubjectMapping subjectMapping = new SubjectMapping();
//        subjectMapping.setCategoryId(categoryBO.getId());
//        // 查mapping表拿到分类下的label
//        List<SubjectMapping> subjectMappingList = subjectMappingService.queryLabelId(subjectMapping);
//        if (CollectionUtils.isEmpty(subjectMappingList)) {  // 如果分类下无标签，直接返回空
//            return null;
//        }
//        // 拿到所有labelId
//        List<Long> labelIds = subjectMappingList.stream().map(SubjectMapping::getLabelId).collect(Collectors.toList());

//        // 根据拿到的labelId中label表中查询
//        List<SubjectLabel> subjectLabelList = subjectLabelService.batchQueryById(labelIds);

        // todo 改为 直接根据categoryId去subject_label表查标签
        SubjectLabel subjectLabel = new SubjectLabel();
        subjectLabel.setCategoryId(categoryBO.getId());
        List<SubjectLabel> subjectLabelList = subjectLabelService.queryByCondition(subjectLabel);

        // labelList转labelBOList
        List<SubjectLabelBO> subjectLabelBOList = SubjectLabelConverter.INSTANCE.convertLabelTOBOList(subjectLabelList);

        labelMap.put(categoryBO.getId(), subjectLabelBOList);
        return labelMap;
    }
}
